# 智能合约设计开发

打开VScode,新建tuna-chaincode.go文件。

# 导入资源包

package main

/* Imports
* 4 utility libraries for handling bytes, reading and writing JSON,
formatting, and string manipulation
* 2 specific Hyperledger Fabric specific libraries for Smart Contracts
*/
import (
    "bytes"
    "encoding/json"
    "fmt"
    "strconv"

    "github.com/hyperledger/fabric/core/chaincode/shim"
    sc "github.com/hyperledger/fabric/protos/peer"
)

// Define the Smart Contract structure
type SmartContract struct {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 定义资产属性

/* Define Tuna structure, with 4 properties.
Structure tags are used by encoding/json library
*/
type Tuna struct {
    Vessel    string `json:"vessel"`
    Timestamp string `json:"timestamp"`
    Location  string `json:"location"`
    Holder    string `json:"holder"`
}
1
2
3
4
5
6
7
8
9

# Invoke方法

/*
 * The Invoke method *
 called when an application requests to run the Smart Contract "tuna-chaincode"
 The app also specifies the specific smart contract function to call with args
*/
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {

    // Retrieve the requested Smart Contract function and arguments
    function, args := APIstub.GetFunctionAndParameters()
    // Route to the appropriate handler function to interact with the ledger
    if function == "queryTuna" {
        return s.queryTuna(APIstub, args)
    } else if function == "initLedger" {
        return s.initLedger(APIstub)
    } else if function == "recordTuna" {
        return s.recordTuna(APIstub, args)
    } else if function == "queryAllTuna" {
        return s.queryAllTuna(APIstub)
    } else if function == "changeTunaHolder" {
        return s.changeTunaHolder(APIstub, args)
    }
    return shim.Error("Invalid Smart Contract function name.")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# query方法

/*
 * The queryTuna method *
Used to view the records of one particular tuna
It takes one argument -- the key for the tuna in question
*/
func (s *SmartContract) queryTuna(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

    if len(args) != 1 {
        return shim.Error("Incorrect number of arguments. Expecting 1")
    }

    tunaAsBytes, _ := APIstub.GetState(args[0])
    if tunaAsBytes == nil {
        return shim.Error("Could not locate tuna")
    }
    return shim.Success(tunaAsBytes)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# initLedger

/*
 * The initLedger method *
Will add test data (10 tuna catches)to our network
*/
func (s *SmartContract) initLedger(APIstub shim.ChaincodeStubInterface) sc.Response {
    tuna := []Tuna{
        Tuna{Vessel: "923F", Location: "67.0006, -70.5476", Timestamp: "1504054225", Holder: "Miriam"},
        Tuna{Vessel: "M83T", Location: "91.2395, -49.4594", Timestamp: "1504057825", Holder: "Dave"},
        Tuna{Vessel: "T012", Location: "58.0148, 59.01391", Timestamp: "1493517025", Holder: "Igor"},
        Tuna{Vessel: "P490", Location: "-45.0945, 0.7949", Timestamp: "1496105425", Holder: "Amalea"},
        Tuna{Vessel: "S439", Location: "-107.6043, 19.5003", Timestamp: "1493512301", Holder: "Rafa"},
        Tuna{Vessel: "J205", Location: "-155.2304, -15.8723", Timestamp: "1494117101", Holder: "Shen"},
        Tuna{Vessel: "S22L", Location: "103.8842, 22.1277", Timestamp: "1496104301", Holder: "Leila"},
        Tuna{Vessel: "EI89", Location: "-132.3207, -34.0983", Timestamp: "1485066691", Holder: "Yuan"},
        Tuna{Vessel: "129R", Location: "153.0054, 12.6429", Timestamp: "1485153091", Holder: "Carlo"},
        Tuna{Vessel: "49W4", Location: "51.9435, 8.2735", Timestamp: "1487745091", Holder: "Fatima"},
    }

    for i := 0; i < len(tuna); i++ {
        fmt.Println("i is ", i)
        tunaAsBytes, _ := json.Marshal(tuna[i])
        APIstub.PutState(strconv.Itoa(i+1), tunaAsBytes)
        fmt.Println("Added", tuna[i])
    }

    return shim.Success(nil)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# record

/*
 * The recordTuna method *
Fisherman like Sarah would use to record each of her tuna catches.
This method takes in five arguments (attributes to be saved in the ledger).
*/
func (s *SmartContract) recordTuna(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

    if len(args) != 5 {
        return shim.Error("Incorrect number of arguments. Expecting 5")
    }

    var tuna = Tuna{Vessel: args[1], Location: args[2], Timestamp: args[3], Holder: args[4]}

    tunaAsBytes, _ := json.Marshal(tuna)
    err := APIstub.PutState(args[0], tunaAsBytes)
    if err != nil {
        return shim.Error(fmt.Sprintf("Failed to record tuna catch: %s", args[0]))
    }

    return shim.Success(nil)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# QueryAll

/*
 * The queryAllTuna method *
allows for assessing all the records added to the ledger(all tuna catches)
This method does not take any arguments. Returns JSON string containing results.
*/
func (s *SmartContract) queryAllTuna(APIstub shim.ChaincodeStubInterface) sc.Response {

    startKey := "0"
    endKey := "999"

    resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
    if err != nil {
        return shim.Error(err.Error())
    }
    defer resultsIterator.Close()

    // buffer is a JSON array containing QueryResults
    var buffer bytes.Buffer
    buffer.WriteString("[")

    bArrayMemberAlreadyWritten := false
    for resultsIterator.HasNext() {
        queryResponse, err := resultsIterator.Next()
        if err != nil {
            return shim.Error(err.Error())
        }
        // Add comma before array members,suppress it for the first array member
        if bArrayMemberAlreadyWritten == true {
            buffer.WriteString(",")
        }
        buffer.WriteString("{\"Key\":")
        buffer.WriteString("\"")
        buffer.WriteString(queryResponse.Key)
        buffer.WriteString("\"")

        buffer.WriteString(", \"Record\":")
        // Record is a JSON object, so we write as-is
        buffer.WriteString(string(queryResponse.Value))
        buffer.WriteString("}")
        bArrayMemberAlreadyWritten = true
    }
    buffer.WriteString("]")

    fmt.Printf("- queryAllTuna:\n%s\n", buffer.String())

    return shim.Success(buffer.Bytes())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# changeHolder

/*
 * The changeTunaHolder method *
The data in the world state can be updated with who has possession.
This function takes in 2 arguments, tuna id and new holder name.
*/
func (s *SmartContract) changeTunaHolder(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

    if len(args) != 2 {
        return shim.Error("Incorrect number of arguments. Expecting 2")
    }

    tunaAsBytes, _ := APIstub.GetState(args[0])
    if tunaAsBytes == nil {
        return shim.Error("Could not locate tuna")
    }
    tuna := Tuna{}

    json.Unmarshal(tunaAsBytes, &tuna)
    // Normally check that the specified argument is a valid holder of tuna
    // we are skipping this check for this example
    tuna.Holder = args[1]

    tunaAsBytes, _ = json.Marshal(tuna)
    err := APIstub.PutState(args[0], tunaAsBytes)
    if err != nil {
        return shim.Error(fmt.Sprintf("Failed to change tuna holder: %s", args[0]))
    }

    return shim.Success(nil)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Main

/*
 * main function *
calls the Start function
The main function starts the chaincode in the container during instantiation.
*/
func main() {

    // Create a new Smart Contract
    err := shim.Start(new(SmartContract))
    if err != nil {
        fmt.Printf("Error creating new Smart Contract: %s", err)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

完整的链码程序,请参见tuna-chaincode.go

Last Updated: 3/9/2020, 9:27:52 PM